﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace Lr
{
    /// <summary>
    /// Provides extension methods to interact with <see cref="Object"/>s.
    /// </summary>
    public static class ObjectExtensions
    {
        #region ToString
        /// <summary>
        /// Wrapper method for <see cref="ToStringHelper.For"/>.
        /// Safely converts an object to a string representation, when an exception is thrown, or when the object is null the method won't fail, 
        /// but will print out the exception or null-string.
        /// </summary>
        /// <typeparam name="TObject">The type of the object.</typeparam>
        /// <param name="object">The object to be represented as a string.</param>
        /// <returns>Returns a string that represents the current object.</returns>
        public static string ToStringSafe<TObject>(this TObject @object)
        {
            return ToStringHelper.For<TObject>(@object);
        }


        /// <summary>
        /// Wrapper method for <see cref="ToStringHelper.For"/>.
        /// Safely converts an object to a string representation, when an exception is thrown, or when the object is null the method won't fail, 
        /// but will print out the exception or null-string.
        /// </summary>
        /// <typeparam name="TObject">The type of the object.</typeparam>
        /// <param name="object">The object to be represented as a string.</param>
        /// <param name="displayValueSelector"><see cref="Expression"/> that defines how the value will be presented.</param>
        /// <returns>Returns a string that represents the current object.</returns>
        public static string ToStringSafe<TObject>(this TObject @object, Func<TObject, string> displayValueSelector)
        {
            return ToStringHelper.For<TObject>(@object, displayValueSelector);
        }
        #endregion

        #region Debugging
        /// <summary>
        /// Returns a <see cref="String"/> representation of the given object for debugging purposes.
        /// </summary>
        /// <typeparam name="TObject">The type of the object.</typeparam>
        /// <param name="object">The object to be represented as a debug string.</param>
        /// <param name="bindingFlags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
        /// <returns>Returns a <see cref="String"/> representation of the given object for debugging purposes.</returns>
        public static string ToDebugString<TObject>(this TObject @object, BindingFlags? bindingFlags = null)
        {
            var objectType = typeof(TObject);
            var properties = !bindingFlags.HasValue ? objectType.GetProperties() : objectType.GetProperties(bindingFlags.Value);
            var propertyStrings = new List<string>();
            var toFriendlyStringTypes = new List<Type>() {                 
            };

            var debugString = new StringBuilder();
            try {

                debugString.Append("[{0}".FormatWith(objectType.ToFriendlyName()));

                foreach (var property in properties) {
                    try {
                        var value = !bindingFlags.HasValue 
                            ? objectType.GetProperty(property.Name).GetValue(@object, null)
                            : objectType.GetProperty(property.Name, bindingFlags.Value).GetValue(@object, null);

                        if (value is ICollection) {
                            value = value.GetType().ToFriendlyName();
                        }

                        propertyStrings.Add("{0}='{1}'".FormatWith(property.Name, value));
                    } catch (Exception ex) {
                        propertyStrings.Add("{0}='ex!{1}'".FormatWith(property.Name, ex.GetType().ToFriendlyName()));
                    }                    
                }
            } catch (Exception ex) {
                propertyStrings.Add("ex!{0}".FormatWith(ex.GetType().ToFriendlyName()));
            }

            if (propertyStrings.Count > 0) {
                debugString.Append(": ");
            }
            debugString.Append(propertyStrings.Flatten("; ", "", ""));
            debugString.Append("]");

            return debugString.ToString();
        }
        #endregion
    }
}
